// SessionLauncher.h

#pragma once
#include <vcclr.h>
#include <msclr/lock.h>
#include <windows.h>
#include <stdio.h>
#include <atlbase.h>
#include <atlalloc.h>
#include <shlwapi.h>



using namespace System;
using namespace System::Threading;
using namespace System::Threading::Tasks;

using namespace msclr; //lock



namespace Sustainalytics {
	namespace OS {

		//managed

		[Serializable]
		public ref class ProcessLaunchException : public Exception {
		public:
			int ErrorCode;
			int InternalErrorCode;
			ProcessLaunchException(int errorCode, System::ComponentModel::Win32Exception^  innerException) : Exception("processLaunchException", innerException){
				if (innerException != nullptr){
					InternalErrorCode = innerException->ErrorCode;
				}
			}
		};

		public ref class LinkedProcess {

		private:
			bool _disposed = false;
			System::UInt32 _processId;
			System::Object^ _waitLock = gcnew System::Object();
			Task^ _waitOnIoPortTask = nullptr;

			//unamaged
			HANDLE _ioPortPtr;
			HANDLE _jobPtr;

			static ProcessLaunchException^ CreateManagedException(int errorCode, int win32ErrorCode);

			static const wchar_t* ToUnmanged(System::String ^str) {

				pin_ptr<const wchar_t> convertedValue = PtrToStringChars(str);  // <-- #include <vcclr.h>

				const wchar_t *constValue = convertedValue;

				return constValue;
			}

			LinkedProcess(HANDLE ioport, HANDLE job, DWORD processId){
				_ioPortPtr = ioport;
				_jobPtr = job;
				_processId = processId;
			}

			Task^ ensureWaitTask() {
				msclr::lock l(_waitLock); //acquire lock
				try
				{
					if (_waitOnIoPortTask == nullptr)
					{
						_waitOnIoPortTask = Task::Factory->StartNew(gcnew Action(this, &LinkedProcess::internalWaitExit), TaskCreationOptions::LongRunning);
					}
				}
				finally{
					l.release();
				}

				return _waitOnIoPortTask;
			}

			void internalWaitExit() {
				DWORD completionCode;
				ULONG_PTR completionKey;
				LPOVERLAPPED overlapped;
				while (GetQueuedCompletionStatus(_ioPortPtr, &completionCode, &completionKey, &overlapped, INFINITE)
					&& !((HANDLE)completionKey == _jobPtr
					&& completionCode == JOB_OBJECT_MSG_ACTIVE_PROCESS_ZERO)) {
					//wait
				}
			}


		public:
			static LinkedProcess^ Start(System::String^ cmdWithParamenters, bool runInConsoleSession, bool hidden);

			property UInt32 ProcessId {
				UInt32 get() {
					return _processId;
				}
			}

			void WaitForExit(){
				ensureWaitTask()->Wait();
			}

			void WaitForExit(int millisecondsTimeout){
				ensureWaitTask()->Wait(millisecondsTimeout);
			}

			void WaitForExit(CancellationToken cancellationToken){
				ensureWaitTask()->Wait(cancellationToken);
			}

			~LinkedProcess() {
				if (_disposed)
					return;
				_disposed = true;
				try {

				}
				finally {
					this->!LinkedProcess();
				}
			}

			!LinkedProcess() {
				try {
					if (_jobPtr)
						CloseHandle(_jobPtr);

					_jobPtr = nullptr;


					if (_ioPortPtr)
						CloseHandle(_ioPortPtr);

					_ioPortPtr = nullptr;

				}
				catch (...){} //not ok
			}
		};


	}
}
